package com.socialsoftware.MediaCommunication.webrtc;

import android.content.Context;
import android.content.Intent;
import android.media.projection.MediaProjection;
import android.util.Log;

import com.socialsoftware.MediaCommunication.model.CallMode;
import com.socialsoftware.MediaCommunication.model.CommunicationMessage;
import com.socialsoftware.MediaCommunication.model.MessageType;
import com.socialsoftware.MediaCommunication.service.WebSocketService;

import org.webrtc.AudioSource;
import org.webrtc.AudioTrack;
import org.webrtc.Camera1Enumerator;
import org.webrtc.CameraEnumerator;
import org.webrtc.DataChannel;
import org.webrtc.DefaultVideoDecoderFactory;
import org.webrtc.DefaultVideoEncoderFactory;
import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.RtpReceiver;
import org.webrtc.RtpSender;
import org.webrtc.ScreenCapturerAndroid;
import org.webrtc.SessionDescription;
import org.webrtc.SurfaceTextureHelper;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;

import java.util.ArrayList;
import java.util.List;

/**
 * Manager class for WebRTC functionality
 */
public class WebRTCManager {
    private static final String TAG = "WebRTCManager";
    
    private final Context context;
    private final WebSocketService webSocketService;
    private final WebRTCListener listener;
    
    private PeerConnectionFactory peerConnectionFactory;
    private PeerConnection peerConnection;
    private EglBase eglBase;
    private VideoTrack localVideoTrack;
    private AudioTrack localAudioTrack;
    private VideoCapturer videoCapturer;
    private SurfaceTextureHelper surfaceTextureHelper;
    private RtpSender videoSender;
    private boolean isFrontCamera = true;
    private String targetUser;
    private String callMode;
    private boolean isCallActive = false;
    private Intent screenCaptureIntent;
    private boolean isScreenCapturing = false;

    public interface WebRTCListener {
        void onLocalVideoReady(VideoTrack videoTrack);
        void onRemoteVideoReady(VideoTrack videoTrack);
        void onCallConnected();
        void onCallDisconnected();
        void onError(String error);
    }

    public WebRTCManager(Context context, WebSocketService webSocketService, WebRTCListener listener) {
        this.context = context;
        this.webSocketService = webSocketService;
        this.listener = listener;
    }

    public void init() {
        eglBase = EglBase.create();
        
        PeerConnectionFactory.InitializationOptions options =
                PeerConnectionFactory.InitializationOptions.builder(context)
                        .createInitializationOptions();
        PeerConnectionFactory.initialize(options);
        
        peerConnectionFactory = PeerConnectionFactory.builder()
                .setVideoEncoderFactory(new DefaultVideoEncoderFactory(
                        eglBase.getEglBaseContext(),
                        true,
                        true))
                .setVideoDecoderFactory(new DefaultVideoDecoderFactory(eglBase.getEglBaseContext()))
                .createPeerConnectionFactory();
    }

    public EglBase getEglBase() {
        return eglBase;
    }

    public void setCallMode(String mode) {
        if (mode == null || mode.isEmpty()) {
            throw new IllegalArgumentException("通信模式不能为空");
        }
        this.callMode = mode;
    }

    public boolean isVideoMode() {
        return CallMode.VIDEO.getValue().equals(callMode);
    }
    
    public boolean isScreenMode() {
        return CallMode.SCREEN.getValue().equals(callMode);
    }

    public void setScreenCaptureIntent(Intent screenCaptureIntent) {
        this.screenCaptureIntent = screenCaptureIntent;
    }

    public void startCall(String targetUser) {
        if (callMode == null || callMode.isEmpty()) {
            throw new IllegalStateException("必须先设置通信模式");
        }
        
        this.targetUser = targetUser;
        createPeerConnection();
        
        if (isScreenMode()) {
            if (screenCaptureIntent == null) {
                listener.onError("屏幕共享需要先获取屏幕捕获权限");
                return;
            }
            startScreenCapture();
        } else {
            startLocalCapture();
        }
        
        isCallActive = true;
        
        // Send call start message
        CommunicationMessage message = new CommunicationMessage();
        message.setType(MessageType.CALL_START.getValue());
        message.setFromUser(webSocketService.getUsername());
        message.setToUser(targetUser);
        message.setMode(callMode);
        webSocketService.sendMessage(message);
    }

    public void answerCall(String caller, String mode) {
        if (mode == null || mode.isEmpty()) {
            throw new IllegalArgumentException("回应通话必须指定通信模式");
        }
        
        this.targetUser = caller;
        this.callMode = mode;
        createPeerConnection();
        
        if (isScreenMode()) {
            // 屏幕共享模式下，被叫方只需要准备接收视频流，不需要获取屏幕捕获权限
            // 仅创建音频流，因为视频流会从主叫方接收
            AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
            localAudioTrack = peerConnectionFactory.createAudioTrack("audio", audioSource);
            
            if (peerConnection != null) {
                peerConnection.addTrack(localAudioTrack);
            }
        } else {
            // 视频或音频通话模式下，正常初始化本地媒体流
            startLocalCapture();
        }
        
        isCallActive = true;
        
        // Send call back message
        CommunicationMessage message = new CommunicationMessage();
        message.setType(MessageType.CALL_BACK.getValue());
        message.setFromUser(webSocketService.getUsername());
        message.setToUser(caller);
        message.setMsg("1");
        message.setMode(callMode);
        webSocketService.sendMessage(message);
    }

    public void rejectCall(String caller) {
        CommunicationMessage message = new CommunicationMessage();
        message.setType(MessageType.CALL_BACK.getValue());
        message.setFromUser(webSocketService.getUsername());
        message.setToUser(caller);
        message.setMsg("0");
        webSocketService.sendMessage(message);
    }

    private void createPeerConnection() {
        List<PeerConnection.IceServer> iceServers = new ArrayList<>();
        iceServers.add(PeerConnection.IceServer.builder("stun:117.50.221.81:3478").createIceServer());
        iceServers.add(PeerConnection.IceServer.builder("turn:117.50.221.81:3478")
                .setUsername("username")
                .setPassword("password")
                .createIceServer());
                
        PeerConnection.RTCConfiguration config = new PeerConnection.RTCConfiguration(iceServers);
        
        peerConnection = peerConnectionFactory.createPeerConnection(config, new PeerConnection.Observer() {
            @Override
            public void onSignalingChange(PeerConnection.SignalingState signalingState) {
                Log.d(TAG, "onSignalingChange: " + signalingState);
            }

            @Override
            public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
                Log.d(TAG, "onIceConnectionChange: " + iceConnectionState);
                if (iceConnectionState == PeerConnection.IceConnectionState.CONNECTED) {
                    listener.onCallConnected();
                } else if (iceConnectionState == PeerConnection.IceConnectionState.DISCONNECTED ||
                        iceConnectionState == PeerConnection.IceConnectionState.FAILED ||
                        iceConnectionState == PeerConnection.IceConnectionState.CLOSED) {
                    listener.onCallDisconnected();
                }
            }

            @Override
            public void onIceConnectionReceivingChange(boolean receiving) {
                Log.d(TAG, "onIceConnectionReceivingChange: " + receiving);
            }

            @Override
            public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
                Log.d(TAG, "onIceGatheringChange: " + iceGatheringState);
            }

            @Override
            public void onIceCandidate(IceCandidate iceCandidate) {
                CommunicationMessage message = new CommunicationMessage();
                message.setType(MessageType.ICE.getValue());
                message.setFromUser(webSocketService.getUsername());
                message.setToUser(targetUser);
                message.setSdpMid(iceCandidate.sdpMid);
                message.setSdpMLineIndex(iceCandidate.sdpMLineIndex);
                message.setCandidate(iceCandidate.sdp);
                message.setMode(callMode);
                webSocketService.sendMessage(message);
            }

            @Override
            public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
                Log.d(TAG, "onIceCandidatesRemoved");
            }

            @Override
            public void onAddStream(MediaStream mediaStream) {
                Log.d(TAG, "onAddStream");
                if (mediaStream.videoTracks.size() > 0 && (isVideoMode() || isScreenMode())) {
                    VideoTrack remoteVideoTrack = mediaStream.videoTracks.get(0);
                    listener.onRemoteVideoReady(remoteVideoTrack);
                }
            }

            @Override
            public void onRemoveStream(MediaStream mediaStream) {
                Log.d(TAG, "onRemoveStream");
            }

            @Override
            public void onDataChannel(DataChannel dataChannel) {
                Log.d(TAG, "onDataChannel");
            }

            @Override
            public void onRenegotiationNeeded() {
                Log.d(TAG, "onRenegotiationNeeded");
            }

            @Override
            public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
                Log.d(TAG, "onAddTrack");
            }
        });
    }

    private void startLocalCapture() {
        if (isVideoMode()) {
            // Video call mode
            videoCapturer = createCameraCapturer(isFrontCamera);
            VideoSource videoSource = peerConnectionFactory.createVideoSource(false);
            surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBase.getEglBaseContext());
            videoCapturer.initialize(surfaceTextureHelper, context, videoSource.getCapturerObserver());
            videoCapturer.startCapture(640, 480, 30);
            localVideoTrack = peerConnectionFactory.createVideoTrack("video", videoSource);
            listener.onLocalVideoReady(localVideoTrack);
        }

        // Audio part (needed for both video and audio calls)
        AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
        localAudioTrack = peerConnectionFactory.createAudioTrack("audio", audioSource);

        if (peerConnection != null) {
            if ((isVideoMode() || isScreenMode()) && localVideoTrack != null) {
                videoSender = peerConnection.addTrack(localVideoTrack);
            }
            peerConnection.addTrack(localAudioTrack);
        }
    }
    
    private void startScreenCapture() {
        if (screenCaptureIntent == null) {
            listener.onError("屏幕共享需要先获取屏幕捕获权限");
            return;
        }
        
        isScreenCapturing = true;
        
        // Create screen capturer
        videoCapturer = new ScreenCapturerAndroid(
                screenCaptureIntent,
                new MediaProjection.Callback() {
                    @Override
                    public void onStop() {
                        Log.d(TAG, "Screen sharing stopped");
                        hangup();
                    }
                });
        
        VideoSource videoSource = peerConnectionFactory.createVideoSource(false);
        surfaceTextureHelper = SurfaceTextureHelper.create("ScreenCaptureThread", eglBase.getEglBaseContext());
        videoCapturer.initialize(surfaceTextureHelper, context, videoSource.getCapturerObserver());
        videoCapturer.startCapture(1280, 720, 15);
        
        localVideoTrack = peerConnectionFactory.createVideoTrack("screen", videoSource);
        listener.onLocalVideoReady(localVideoTrack);
        
        // Audio part
        AudioSource audioSource = peerConnectionFactory.createAudioSource(new MediaConstraints());
        localAudioTrack = peerConnectionFactory.createAudioTrack("audio", audioSource);
        
        if (peerConnection != null) {
            videoSender = peerConnection.addTrack(localVideoTrack);
            peerConnection.addTrack(localAudioTrack);
        }
    }

    public void switchCamera() {
        if (!isVideoMode() || videoCapturer == null || isScreenCapturing) return;
        
        isFrontCamera = !isFrontCamera;
        
        try {
            videoCapturer.stopCapture();
        } catch (InterruptedException e) {
            Log.e(TAG, "Failed to stop capture", e);
            return;
        }
        
        videoCapturer.dispose();
        videoCapturer = null;
        
        if (surfaceTextureHelper != null) {
            surfaceTextureHelper.dispose();
            surfaceTextureHelper = null;
        }
        
        // Create new camera capturer
        videoCapturer = createCameraCapturer(isFrontCamera);
        VideoSource newVideoSource = peerConnectionFactory.createVideoSource(false);
        surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBase.getEglBaseContext());
        videoCapturer.initialize(surfaceTextureHelper, context, newVideoSource.getCapturerObserver());
        videoCapturer.startCapture(640, 480, 30);
        
        VideoTrack newVideoTrack = peerConnectionFactory.createVideoTrack("video", newVideoSource);
        listener.onLocalVideoReady(newVideoTrack);
        
        if (videoSender != null) {
            boolean success = videoSender.setTrack(newVideoTrack, true); // Auto release old track
            if (success) {
                localVideoTrack = newVideoTrack;
            }
        } else if (peerConnection != null) {
            videoSender = peerConnection.addTrack(newVideoTrack);
        }
    }

    private VideoCapturer createCameraCapturer(boolean isFront) {
        CameraEnumerator enumerator = new Camera1Enumerator(false);
        String[] deviceNames = enumerator.getDeviceNames();
        
        // Try to find the requested camera
        for (String deviceName : deviceNames) {
            if (isFront && enumerator.isFrontFacing(deviceName)) {
                return enumerator.createCapturer(deviceName, null);
            } else if (!isFront && enumerator.isBackFacing(deviceName)) {
                return enumerator.createCapturer(deviceName, null);
            }
        }
        
        // If not found, use first available camera
        if (deviceNames.length > 0) {
            return enumerator.createCapturer(deviceNames[0], null);
        }
        
        return null;
    }

    public void createOffer() {
        MediaConstraints constraints = new MediaConstraints();
        constraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", (isVideoMode() || isScreenMode()) ? "true" : "false"));
        constraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
        
        peerConnection.createOffer(new SimpleSdpObserver() {
            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                peerConnection.setLocalDescription(new SimpleSdpObserver(), sessionDescription);
                
                CommunicationMessage message = new CommunicationMessage();
                message.setType(MessageType.OFFER.getValue());
                message.setFromUser(webSocketService.getUsername());
                message.setToUser(targetUser);
                message.setSdp(sessionDescription.description);
                message.setMode(callMode);
                webSocketService.sendMessage(message);
            }
            
            @Override
            public void onCreateFailure(String s) {
                listener.onError("Failed to create offer: " + s);
            }
        }, constraints);
    }

    public void handleOffer(String sdp, String mode) {
        if (mode == null || mode.isEmpty()) {
            throw new IllegalArgumentException("处理Offer必须指定通信模式");
        }
        
        this.callMode = mode;
        SessionDescription offer = new SessionDescription(SessionDescription.Type.OFFER, sdp);
        peerConnection.setRemoteDescription(new SimpleSdpObserver() {
            @Override
            public void onSetSuccess() {
                createAnswer();
            }
            
            @Override
            public void onSetFailure(String s) {
                listener.onError("Failed to set remote offer: " + s);
            }
        }, offer);
    }

    private void createAnswer() {
        MediaConstraints constraints = new MediaConstraints();
        constraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", (isVideoMode() || isScreenMode()) ? "true" : "false"));
        constraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
        
        peerConnection.createAnswer(new SimpleSdpObserver() {
            @Override
            public void onCreateSuccess(SessionDescription sessionDescription) {
                peerConnection.setLocalDescription(new SimpleSdpObserver(), sessionDescription);
                
                CommunicationMessage message = new CommunicationMessage();
                message.setType(MessageType.ANSWER.getValue());
                message.setFromUser(webSocketService.getUsername());
                message.setToUser(targetUser);
                message.setSdp(sessionDescription.description);
                message.setMode(callMode);
                webSocketService.sendMessage(message);
            }
            
            @Override
            public void onCreateFailure(String s) {
                listener.onError("Failed to create answer: " + s);
            }
        }, constraints);
    }

    public void handleAnswer(String sdp) {
        SessionDescription answer = new SessionDescription(SessionDescription.Type.ANSWER, sdp);
        peerConnection.setRemoteDescription(new SimpleSdpObserver(), answer);
    }

    public void handleIceCandidate(String sdpMid, int sdpMLineIndex, String sdp) {
        IceCandidate candidate = new IceCandidate(sdpMid, sdpMLineIndex, sdp);
        peerConnection.addIceCandidate(candidate);
    }

    public void hangup() {
        if (isCallActive && targetUser != null) {
            CommunicationMessage message = new CommunicationMessage();
            message.setType(MessageType.HANGUP.getValue());
            message.setFromUser(webSocketService.getUsername());
            message.setToUser(targetUser);
            message.setMode(callMode);
            webSocketService.sendMessage(message);
        }
        
        cleanup();
    }

    public void cleanup() {
        isCallActive = false;
        isScreenCapturing = false;
        targetUser = null;
        callMode = null;
        
        if (videoCapturer != null) {
            try {
                videoCapturer.stopCapture();
            } catch (InterruptedException e) {
                Log.e(TAG, "Failed to stop capture", e);
            }
            videoCapturer.dispose();
            videoCapturer = null;
        }
        
        if (localVideoTrack != null) {
            localVideoTrack.dispose();
            localVideoTrack = null;
        }
        
        if (localAudioTrack != null) {
            localAudioTrack.dispose();
            localAudioTrack = null;
        }
        
        if (surfaceTextureHelper != null) {
            surfaceTextureHelper.dispose();
            surfaceTextureHelper = null;
        }
        
        if (peerConnection != null) {
            peerConnection.close();
            peerConnection = null;
        }
        
        videoSender = null;
    }

    public void release() {
        cleanup();
        
        if (peerConnectionFactory != null) {
            peerConnectionFactory.dispose();
            peerConnectionFactory = null;
        }
        
        if (eglBase != null) {
            eglBase.release();
            eglBase = null;
        }
    }

    public boolean isCallActive() {
        return isCallActive;
    }
    /**
     * Mute or unmute the microphone during a call
     * @param mute true to mute, false to unmute
     */
    public void setMicrophoneMute(boolean mute) {
        if (localAudioTrack != null) {
            localAudioTrack.setEnabled(!mute);
        }
    }
} 